/*
  webpack.config.js webpack的配置文件
    作用：指示webpack干哪些活（当你运行webpack指令时，会加载里面的配置）

    所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs.

*/

/*
  loader: 1.下载  2.使用（配置loader）
  plugins:1.下载  2.引入  3.使用
*/

/*  HMR:  hot module replacement 热模块替换/模块热替换
      作用：一个模块发生变化，只会重新打包这一个模块（而不是打包所有模块）
      极大提升构建速度
      样式文件：默认开启HMR
      js文件：默认情况下不能使用HMR -——》需要修改JS代码，添加支持HMR功能的代码
        注意：HMR功能对js的处理，只能处理非入口js文件的其他文件
      html文件：默认不能使用HMR功能，同时会导致问题，html文件不能被热更新了~ (不用做HMR功能)
        解决：entry: ['./src/js/index.js', './src/index.html']
*/

/*
  缓存：
    babel缓存
      配置  cacheDirectory: true
      --》让第二次打包速度更快
    文件资源缓存
      修改文件名 hash：每次webpack打包会生成一个唯一的hash值
        问题：因为js和css同时使用一个hash值 如果重新打包会导致所有缓存失效（可能我之改动一个文件）
      chunkhash:根据chunk生成hash值。如果打包来源同一个chunk,那么hash值一样
        问题：js和css的hash值还是一样
        因为css是在js中被引入的，所以同属于一个chunk
      contenthash：根据文件的内容生成hash值。不同文件的hash值一定不一样
      --》让代码上线运行缓存更好使用
*/

/*
  tree shaking:去除无用代码
    前提：1.必须使用ES6模块化 2.开启production环境
    作用：减少代码体积

  在package.json中配置：
    "sideEffects":false 所有代码都没有副作用（都可以进行tree shaking）
      问题：可能会把css /@babel/polyfill（副作用）文件干掉
      解决："sideEffects":["*.css"]

*/

/*
  PWA:渐进式网络开发应用程序（离线可访问）
  workbox --> workbox-webpack-plugin
*/

//  resolve用来拼接绝对路径的方法
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const ESlintWebpackPlugin = require('eslint-webpack-plugin')
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
// 设置nodejs坏境变量
process.env.NODE_ENV = 'production'
module.exports = {
  //  webpack配置
  //  入口起点
  //  单入口
  entry: ['./src/js/index.js', './src/index.html'],
  //  多入口
  // entry: {
  //   main: './src/js/index.js',
  //   print: './src/js/print.js'
  // },
  //  输出
  output: {
    //  输出文件名 name取文件名
    filename: 'js/[name].[contenthash:10].js',
    //  输出路径
    //  __dirname nodejs的变量，代表当前文件的目录绝对路径
    path: resolve(__dirname, 'build'),
  },
  //  loader的配置
  module: {
    rules: [
      //  详细loader配置
      //  不同文件必须配置不同loader处理
      {
        //  匹配哪些文件
        test: /\.css$/,
        //  使用哪些loader进行处理
        //  要使用多个loader处理用use
        use: [
          //  use数组中loader执行顺序：从右到左，从下到上 依次执行
          //  创建style标签，将js中的样式资源插入进行，添加到head中生效
          // 'style-loader',
          //  这个loader取代style-loader.作用：提取js中的css成单独文件
          //  通过link的方式引入css,防止出现闪屏现象
          MiniCssExtractPlugin.loader,
          //  将css文件变成commonjs模块加载到js中，里面内容是样式字符串
          'css-loader',
          //  css兼容性处理：postcss --> postcss-loader postcss-preset-env
          //  帮postcss找到package.json中browserslist里面的配置，通过配置加载指定的css兼容性样式

          // "browserslist":{
          //  开发坏境：设置node坏境变量：process.env.NODE_ENV=development
          //   "development":[
          //     "last 1 chrome version",
          //     "last 1 firefox version",
          //     "last 1 safari version"
          //   ],
          //  生产坏境：默认是看生产坏境
          //   "production":[
          //     ">0.2%",
          //     "not dead",
          //     "not op_mini all"
          //   ]
          // }

          //  postcss-loader
          //  修改loader的配置
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  //  postcss插件
                  'postcss-preset-env'
                ]
              }
            }
          }
        ]
      },
      {
        //  匹配哪些文件
        test: /\.less$/,
        //  使用哪些loader进行处理
        use: [
          // 'style-loader',
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  //  postcss插件
                  'postcss-preset-env'
                ]
              }
            }
          },
          //  将less编译成css文件
          //  需要下载 less-loader和less
          'less-loader'
        ],
      },
      //  webpack5之前的写法
      // {
      //   //  处理图片资源
      //   test: /\.(jpg|png|gif)$/,
      //   //  使用一个loader
      //   //  下载url-loader file-loader
      //   loader: 'url-loader',
      //   options: {
      //     //  图片大小小于8kb，就会被base64处理
      //     //  优点：减少请求数量（减轻服务器压力）
      //     //  缺点：图片体积会更大（文件请求速度会变慢）
      //     limit: 8 * 1024,
      //     //  问题：因为url-loader默认使用ES6模块化解析，而html-loader引入图片是commonjs
      //     //  解析时会出现问题：[object Module]
      //     //  解决：关闭url-loader的es6模块化，使用commonjs解析
      //     esModule: false,
      //     //  对图片资源重命名
      //     name: 'img/[hash:10].[ext]',
      //   },
      //   type: 'javascript/auto'
      // },

      //  webpack5 使用asset-module
      {
        test: /\.(jpg|png|gif)$/,
        type: 'asset',
        parser: {
          //  转base64的条件
          dataUrlCondition: {
            maxSize: 8 * 1024
          }
        },
        generator: {
          filename: 'img/[hash:10].[ext]'
        }
      },
      {
        test: /\.html$/,
        //  处理html文件img图片（负责引入img，从而能被css-loader/url-loader进行处理）
        loader: 'html-loader'
      },
      //  打包其他资源(除了css/less/js/png/rng/gif等已经处理过的资源)
      // {
      //   exclude: /\.(css|less|js|png|rng|gif)$/,
      //   loader: 'file-loader',
      //   options: {
      //     name: '[hash:10].[ext]'
      //   }
      // },

      //  打包字体图标资源
      {
        test: /\.(ttf|eot|woff2)$/,
        type: 'asset/resource',
        generator: {
          filename: 'font/[hash:10].[ext]'
        }
      },
      // {
      //    老版本的eslint检查 webpack5已废用
      //   //  语法检查  eslint-loader eslint
      //   //  注意：只检查自己写的源代码，第三方的库是不用检查的
      //   //  设置检查规则：
      //   //    package.json中eslintConfig设置
      //   //    "eslintConfig": {
      //   //       "extends": "airbnb-base"
      //   //    }
      //   //    airbnb --> eslint-config-airbnb eslint-plugin-import eslint
      //   test: /\.js$/,
      //   loader: 'eslint-loader',
      //   exclude: resolve(__dirname, 'node_modules'),
      //   options: {
      //     //  自动修复eslint错误
      //     fix: true
      //   }
      // }

      //  1.基本js兼容性处理 babel-loader  @babel/preset-env @babel/core 1
      //  问题：只能转化基本语法，如promise不能转化
      //  2.全部js兼容性处理 @babel/polyfill 0
      //  问题：我只要解决部分兼容性问题，但是将所有的兼容性代码全部引入，体积太大
      //  3.需要做兼容性处理的就做 按需加载 core-js 1
      {
        test: /\.js$/,
        exclude: resolve(__dirname, 'node_modules'),
        use: [
          {
            //  开启多进程打包
            //  进程启动大概600ms，进程通信也需要开销
            //  只有工作消耗时间比较长，才需要多进程打包
            loader: 'thread-loader',
            options: {
              workers: 2 // 进程2
            }
          },
          {
            loader: 'babel-loader',
            options: {
              //  预设：指示bable做什么样的兼容性处理
              presets: [
                [
                  '@babel/preset-env',
                  {
                    //  按需加载
                    useBuiltIns: 'usage',
                    corejs: {
                      version: 3
                    },
                    //  指定兼容性做到那个版本浏览器
                    targets: {
                      chrome: '60',
                      firefox: '60',
                      ie: '9',
                      safari: '17',
                      edge: '10'
                    }
                  }
                ]
              ],
              //  开启babel缓存
              //  第二次构建时，会读取之前的缓存
              cacheDirectory: true
            }
          }
        ]
      }
    ]
  },
  //  plugins的配置
  plugins: [
    //  详细plugins的配置
    //  html-webpack-plugin
    //  默认会创建一个html文件，自动引入打包输出的所有资源（JS/CSS）
    //  需求：需要有结构的HTML文件
    new HtmlWebpackPlugin({
      //  复制./src/index.html文件，自动引入打包输出的所有资源（JS/CSS）
      template: './src/index.html',
      //  压缩html
      minify: {
        //  移除空格
        collapseWhitespace: true,
        //  移除注释
        removeComments: true
      }
    }),
    new MiniCssExtractPlugin({
      //  对输出的css进行重命名
      filename: 'css/built.[contenthash:10].css'
    }),
    //  压缩css
    new OptimizeCssAssetsWebpackPlugin(),
    new ESlintWebpackPlugin({
      //  自动修复
      fix: true
    }),
    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1.帮助serviceworker快速启动
        2.删除旧的serviceworker
        生成一个serviceworker配置文件
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  //  可以将node_modules中的代码单独打包一个chunk输出
  //  自动分析多入口chunk中有没有公共的文件。如果有就打包成单独一个chunk
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  //  模式
  //  生产坏境会自动压缩js代码
  mode: 'development', // 开发模式
  // mode:'production'

  //  开发服务器 devServer: 用来自动化（自动编译，自动打开浏览器，自动刷新浏览器）
  //  特点：只会在内存中编译打包，不会有任何输出
  //  启动devServer指令为：npx webpack-dev-server
  devServer: {
    //  项目构建后的路径
    static: resolve(__dirname, 'build'),
    //  启动gzip压缩
    compress: true,
    //  端口号
    port: 3000,
    //  自动打开浏览器
    open: true,
    //  开启HMR功能
    hot: true
  },
  devtool: 'eval-source-map',
  externals: {
    //  在开发中有些包是通过cdn引入的就不需要对包进行打包
    //  拒绝jQuery被打包进来
    jquery: 'jQuery'
  }
}

/*
  source-map: 一种提供源代码到构建后代码映射技术  （如果构建后的代码出错了，通过映射关系追踪到源代码的错误）
    [inline-|hidden-|eval-] [nosources-] [cheap-[module-]]source-map

    source-map: 外部
      错误代码的准确信息 和 源代码的位置
    inline-source-map:  内联
      只生成一个内联source-map，效果同source-map
    hidden-source-map： 外部
      错误代码错误原因，但是没有错误位置，不能追踪带源代码的错误，只能提示到构建后代码的位置
    eval-source-map： 内联
      每一个文件都生成对应的source-map，都在eval，效果同source-map
    nosources-source-map: 外部
      错误代码的准确信息，但是没有任何源代码信息
    cheap-source-map: 外部
      错误代码的准确信息 和 源代码的位置，只精确到行
    cheap-module-source-map:  外部
      错误代码的准确信息 和 源代码的位置
      module会将loader的source map加入

    内联和外部的区别：1.外部生成了文件，内联没有  2.内联构建速度更快

    开发坏境：速度快，调试更好
      速度快（eval>inline>cheap>...）
        eval-cheap-source-map
        eval-source-map
      调试更友好
        source-map
        cheap-moudle-source-map
        cheap-source-map

      --> eval-source-map / eval-cheap-moudle-source-map
    生产坏境：源代码要不要隐藏？调试要不要更好？
      内联会让代码体积变得非常大，生产坏境不考虑内联
      nosources-source-map  全部隐藏
      hidden-source-map 只隐藏源代码，会提示建构后的代码错误信息

      --> source-map / cheap-module-source-map
*/
